﻿using Hjt212Parser;
using HPSocketCS;
using HpSocketIotGateway.Db;
using HpSocketIotGateway.Facade;
using HpSocketIotGateway.Properties;
using log4net;
using System;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Threading;

namespace HpSocketIotGateway
{
    public partial class TinzDataGateway : ServiceBase
    {
        private static readonly ILog Log = LogManager.GetLogger(typeof(TinzDataGateway));
        /// <summary>
        /// 初始化
        /// </summary>
        public TinzDataGateway()
        {
            try
            {
                InitializeComponent();
                DbRouteAndBackup.InitTable();
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }
        }

        public void Main()
        {
            OnStart(null);
        }
        /// <summary>
        /// Windows服务启动接口，启动后依次运行各回调方法和线程
        /// </summary>
        /// <param name="args"></param>
        protected override void OnStart(string[] args)
        {
            try
            {
                new Thread(StartServerThread) { IsBackground = true }.Start();
                QqMail.Send("服务启动成功!");
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                QqMail.Send(ex.StackTrace);
            }
           
        }

       
        /// <summary>
        /// 服务停止后
        /// </summary>
        protected override void OnStop()
        {
            try
            {
                QqMail.Send("服务停止!");
                Environment.Exit(0);
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }

        }
        //------------OnPrepareListen--------------------------------
        HandleResult OnHpSocketServerPrepareListen(IntPtr soListen)
        {
            return HandleResult.Ok;
        }
        //------------OnAccept------------------------------------
        HandleResult OnHpSocketServerAccept(IntPtr connId, IntPtr pClient)
        {
            string ip = string.Empty;
            ushort port = 0;
            if (!BufferFacade.Server.GetRemoteAddress(connId, ref ip, ref port))
            {
                return HandleResult.Ok;
            }
            BufferFacade.AddSocketClient(connId, ip);
            return HandleResult.Ok;
        }
        //-------------OnSend---------------------------------
        HandleResult OnHpSocketServerSend(IntPtr connId, IntPtr pData, int length)//HandleResult OnHpSocketServerSend(IntPtr connId, byte[] bytes)
        {
            // 服务器发数据了
            return HandleResult.Ok;
        }
        //---------------OnReceive---------------------------------
        HandleResult OnHpSocketServerReceive(IntPtr connId, IntPtr pData, int length)//HandleResult OnHpSocketServerReceive(IntPtr connId, byte[] bytes)
        {
            try
            {
                // 获取客户端ip和端口
                string ip = string.Empty;
                ushort port = 0;
                if (!BufferFacade.Server.GetRemoteAddress(connId, ref ip, ref port))
                {
                    return HandleResult.Ok;
                }
                string message = Marshal.PtrToStringAnsi(pData, length);
                Log.Info("Upline message {SocketID(" + connId + ")[IP(" + ip + ")]}:" + message);
                if (string.IsNullOrWhiteSpace(message))
                {
                    return HandleResult.Ok;
                }
                string mn = Parser.GetStringByKey("MN", message);
                if (string.IsNullOrWhiteSpace(mn))
                {
                    //连续发32次没有MN信息的串就断开
                    //连续发11分钟没有MN信息的串就断开
                    if (MyCounter.IsToTheMax("ClientSocketID:" + connId, 32) || MyTimer.IsToTheTime("ClientSocketID:" + connId, 11))
                    {
                        MyCounter.Remove("ClientSocketID:" + connId);
                        MyTimer.Remove("ClientSocketID:" + connId);
                        BufferFacade.Server.Disconnect(connId);
                        Log.Warn("Suspected attack : address is " + ip + ":" + port);
                    }
                    return HandleResult.Ok;
                }
                //能执行到这里说明有MN，所以将相关计数计时初始化
                MyCounter.UpdateToZero("ClientSocketID:" + connId);
                MyTimer.UpdateToNow("ClientSocketID:" + connId);
                //前端表中合法MN检查：表中mn不是allow就禁止。
                if (Authorization.IsMnProhibited(mn))
                {
                    BufferFacade.Server.Disconnect(connId);
                    Log.Warn(mn + " is prohibited!");
                    return HandleResult.Ok;
                }
                //解决粘包问题
                string[] msgs = message.Split('\n');
                foreach (var msg in msgs)
                {
                    if (string.IsNullOrWhiteSpace(msg) || msg.Length < 16)
                    {
                        continue;
                    }

                    if (!Parser.IsLengthRight(msg) && !Parser.IsHeartBeatPackage(msg))
                    {
                        Log.Warn("SocketID:" + connId + ",IP:" + ip + ",the message is wrong:" + msg);
                        continue;
                    }
                    BufferFacade.AddSocketClient(connId, mn);

                    mn = Parser.GetStringByKey("MN", msg);
                    if (string.IsNullOrEmpty(mn))
                    {
                        continue;
                    }
                    //若相关指令发送太频繁，保持3分钟存一条
                    var cn = Parser.GetStringByKey("CN", msg);
                    var pno = Parser.GetIntByKey("PNO", msg);
                    if (!pno.HasValue)
                    {
                        pno = 9;
                    }

                    if (cn.Equals("2011") || cn.Equals("2051"))//|| cn.Equals("2072"))
                    {
                        if (MyTimer.IsToTheTime(mn + cn + pno, 3, true))
                        {
                            BufferFacade.EnqueueToMessageQueue(msg);
                        }
                        else
                        {
                            Log.Warn("Send too often:" + msg);
                        }
                    }
                    else
                    {
                        BufferFacade.EnqueueToMessageQueue(msg);
                    }
                    //根据qn和flag确定是否应答
                    var qn = Parser.GetDateTimeByKey("QN", msg);
                    if (qn == null)
                    {
                        continue;
                    }
                    var flag = Parser.GetIntByKey("Flag", msg);
                    if (flag.HasValue && IsNeedResponse(flag.Value))
                    {
                        var qnStr = Parser.GetStringByKey("QN", msg);
                        var pw = Parser.GetStringByKey("PW", msg);
                        Response(connId, mn, qnStr, pw);
                    }
                    //根据qn和指令2011及2051决定是否校时
                    Timing(mn, qn.Value, cn);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }
            return HandleResult.Ok;
        }
        //-----------OnClose-------------------------------------
        HandleResult OnClientClose(IntPtr connId, SocketOperation enOperation, int errorCode)
        {
            string mn = BufferFacade.RemoveSocketClient(connId);
            BizStatus.SetOffline(mn);
            Log.Info("SocketID:" + connId + "(MN:" + mn + ") is offline!");
            return HandleResult.Ok;
        }
        //------------OnShutdown------------------------------------
        HandleResult OnHpSocketServerShutdown()
        {
            //BizStatus.SetAllOffline();
            //QqMail.Send("The Server is Shutdowned!");
            Log.Error("The Server is Shutdowned!");
            // 服务关闭了
            return HandleResult.Ok;
        }
        /// <summary>
        /// 启动服务进程，接收数据
        /// </summary>
        private void StartServerThread(object state)
        {
            try
            {
                BufferFacade.Server.IpAddress = Settings.Default.ServerIP;
                BufferFacade.Server.Port = Settings.Default.ServerPort;

                //绑定事件
                //绑定监听地址前触发
                BufferFacade.Server.OnPrepareListen += OnHpSocketServerPrepareListen;
                //客户端连接请求被接受后触发
                BufferFacade.Server.OnAccept += OnHpSocketServerAccept;
                //发送消息后触发
                //TcpServerEvent.OnSendEventHandler
                BufferFacade.Server.OnSend += OnHpSocketServerSend;
                //收到消息后触发
                //TcpServerEvent.OnReceiveEventHandler
                BufferFacade.Server.OnReceive += OnHpSocketServerReceive;
                //连接关闭后触发（服务端的连接通常是多个，只要某一个连接关闭了都会触发）
                BufferFacade.Server.OnClose += OnClientClose;
                //组件停止后触发
                BufferFacade.Server.OnShutdown += OnHpSocketServerShutdown;
                BufferFacade.Server.MarkSilence = true;
                // 启动服务
                if (BufferFacade.Server.Start())
                {
                    Log.Info("The Server Startup is Success:" + Settings.Default.ServerIP + ":" + Settings.Default.ServerPort);

                    new Thread(SentCtrCmdThread) { IsBackground = true }.Start();
                    new Thread(SentDataThread) { IsBackground = true }.Start();
                    new Thread(DisconnectSilenceConnectionsThread) { IsBackground = true }.Start();
                    new Thread(Consumer.Consume4Persistence) { IsBackground = true }.Start();
                    new Thread(Consumer.Consume4Persistence4GatewayResponse) { IsBackground = true }.Start();
                }
                else
                {
                    Log.Error(new Exception("服务启动失败!"));
                    QqMail.Send("ERROR:服务启动失败，服务停止，请关注!");
                    Environment.Exit(0);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                QqMail.Send(ex.StackTrace);
                Environment.Exit(0);
            }
        }
        private void SentCtrCmdThread(object state)
        {
            ThreadUtil.Sleep(10 * 1000);
            while (true)
            {
                try
                {
                    BizSendCtrCmd.Send();
                    if (MyTimer.IsToTheTime("TinzDataGateway.SentCtrCmdThread", 10))
                    {
                        ThreadUtil.Sleep(1000);
                        if (Settings.Default.BackupMonthsTo1Table > 0)
                        {
                            BackupProcessedRecord.BackupSentCmd();
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log.Error(ex);
                }
                finally
                {
                    ThreadUtil.Sleep(1000);
                }
            }
        }
        private void SentDataThread(object state)
        {
            ThreadUtil.Sleep(10 * 1000);
            while (true)
            {
                try
                {
                    BizSendData.SendByShake();

                    if (MyTimer.IsToTheTime("TinzDataGateway.SentDataThread", 5))
                    {
                        ThreadUtil.Sleep(1000);
                        if (Settings.Default.BackupMonthsTo1Table > 0)
                        {
                            BackupProcessedRecord.BackupSentData();
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log.Error(ex);
                }
                finally
                {
                    ThreadUtil.Sleep(2000);
                }
            }
        }
        private void DisconnectSilenceConnectionsThread(object state)
        {
            ThreadUtil.Sleep(1000);
            while (true)
            {
                try
                {
                    BufferFacade.Server.DisconnectSilenceConnections(1000 * 60 * 21);
                }
                catch (Exception ex)
                {
                    Log.Error(ex);
                }
                finally
                {
                    ThreadUtil.Sleep(1000 * 60);
                }
            }
        }

        private void Timing(string mn, DateTime qn, string cn)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(mn) || string.IsNullOrWhiteSpace(cn))
                {
                    return;
                }

                var now = DateTime.Now;
                var totalMinutes = qn > now ? (qn - now).TotalMinutes : (now - qn).TotalMinutes;

                if (cn.Equals("2011") || cn.Equals("2051"))
                {
                    if (!string.IsNullOrWhiteSpace(mn) && totalMinutes > 10)
                    {
                        var result = "QN=" + now.ToString("yyyyMMddHHmmssfff") + ";ST=32;CN=1012;PW=123456" + ";MN=" + mn
                                     + ";Flag=0;CP=&&SystemTime=" + now.ToString("yyyyMMddHHmmss") + "&&";
                        var wrapByCrc = Crc.WrapByCrc(result, "monitor");
                        BufferFacade.SendMsgToClient(mn, wrapByCrc);
                        BufferFacade.EnqueueToMessageQueue4GatewayResponse(wrapByCrc);
                        Log.Info("Timing  qn:" + qn.ToString("yyyy-MM-dd HH:mm:ss") + ",\nTiming cmd:" + result);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }
        }
        private void Response(IntPtr connId, string mn, string qn, string pw)
        {
            try
            {
                if (!string.IsNullOrWhiteSpace(mn) && !string.IsNullOrWhiteSpace(qn) && connId != IntPtr.Zero)
                {
                    if (string.IsNullOrWhiteSpace(pw))
                    {
                        pw = "123456";
                    }

                    var result = "ST=91" + ";CN=9014;PW=" + pw + ";MN=" + mn + ";Flag=0;CP=&&QN=" + qn + ";ExeRtn=1&&";
                    var wrapByCrc = Crc.WrapByCrc(result, "monitor");
                    BufferFacade.SendMsgToClient(mn, wrapByCrc);
                    BufferFacade.EnqueueToMessageQueue4GatewayResponse(wrapByCrc);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }
        }

        private static bool IsNeedResponse(int i)
        {
            return (i & 1) == 1;
        }

    }
}
